package org.gavin.service.impl;

import lombok.RequiredArgsConstructor;
import org.gavin.attribute.RecommendAttribute;
import org.gavin.entity.dto.Recommend;
import org.gavin.entity.param.RecommendParam;
import org.gavin.enums.RecommendEnums;
import org.gavin.service.RecommendService;
import org.redis.service.RedisKeys;
import org.redis.service.RedisService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * RecommendServiceImpl
 *
 * @author grl
 * @date 2023/12/26
 */
@Service
@RequiredArgsConstructor
public class RecommendServiceImpl implements RecommendService {

    private Long START = 0L;
    private Long END = -1L;

    private final RedisService redisService;

    private final RecommendAttribute recommendAttribute;

    /**
     * 初始化推荐数据
     */
    @Override
    public void initRecommendData() {
        initTopData();
        initNewsData();
        initLikesData();
        initLocationData();
        initDBData();
    }

    private void addList(List<Integer> list, Integer initial,Integer count) {
        for (int i = 0; i < count; i++) {
            list.add(initial + i);
        }
    }

    public void initTopData(){
        // 全部 取消置顶前30分钟
        List<Integer> top = new ArrayList<>();
        addList(top, 1010000,200);
        RecommendEnums topData = RecommendEnums.RECOMMEND_TOP_DATA;
        if(!CollectionUtils.isEmpty(top)){
            redisService.addList(topData, top, top.size());
            top.clear();
        }else{
            redisService.deleteKey(topData);
        }
    }

    public void initLikesData(){
        //前200
        List<Integer> likes = new ArrayList<>();
        addList(likes, 1020000,200);
        RecommendEnums likesData = RecommendEnums.RECOMMEND_LIKES_DATA;
        if(!CollectionUtils.isEmpty(likes)){
            redisService.addList(likesData, likes,likes.size());
            likes.clear();
        }else{
            redisService.deleteKey(likesData);
        }
    }

    public void initNewsData(){
        // 取前200
        List<Integer> newsData = new ArrayList<>();
        addList(newsData, 1030000,500);
        if(!CollectionUtils.isEmpty(newsData)){
            redisService.addList(RecommendEnums.RECOMMEND_NEWS_DATA, newsData,newsData.size());
            newsData.clear();
        }else{
            redisService.deleteKey(RecommendEnums.RECOMMEND_NEWS_DATA);
        }
    }

    public void initLocationData(){
        List<String> provinceList = List.of("陕西省");
        for (String province:provinceList) {
            List<Integer> locationData = new ArrayList<>();
            addList(locationData, 1040000,1000);
            RecommendEnums locationDataKey = RecommendEnums.RECOMMEND_LOCATION_DATA;
            locationDataKey.setKey(province);
            if(!CollectionUtils.isEmpty(locationData)){
                redisService.addSet(locationDataKey, locationData);
                locationData.clear();
            }else{
                redisService.deleteKey(locationDataKey);
            }
        }
    }

    public void initDBData(){
        List<Integer> dbData = new ArrayList<>();
        addList(dbData, 1010000,200);
        addList(dbData, 1020000,200);
        addList(dbData, 1030000,500);
        addList(dbData, 1040000,1000);
        addList(dbData, 1050000,10000);
        if(!CollectionUtils.isEmpty(dbData)){
            redisService.addSet(RecommendEnums.RECOMMEND_DB_DATA, dbData);
            dbData.clear();
        }

    }


    @Override
    public List<Integer> getRecommendData(RecommendParam param){
        List<Integer> resultData = new ArrayList<>();
        // 取用户的历史数据
        Set<Integer> historyData = getHistoryData(param.getUserName());

        List<Integer> resultTop = getResultTopList(historyData);

        if(!CollectionUtils.isEmpty(resultTop)){
            historyData.addAll(resultTop);
            resultData.addAll(resultTop);
        }
        // 取点赞量最高的数据
        List<Integer> resultLikes = getLikesList(historyData);
        if(!CollectionUtils.isEmpty(resultLikes)){
            historyData.addAll(resultLikes);
            resultData.addAll(resultLikes);
        }
        int needSize = 0;
        RecommendAttribute.ViewCount view = recommendAttribute.getView();
        int beforeTotal = view.getTop() + view.getLikes();
        if(beforeTotal > resultData.size()){
            needSize = beforeTotal - resultData.size();
        }
        // 获取最新的
        List<Integer> resultNews = getResultNewsList(historyData,needSize);
        if(!CollectionUtils.isEmpty(resultNews)){
            historyData.addAll(resultNews);
            resultData.addAll(resultNews);
        }
        if(!CollectionUtils.isEmpty(resultData)){
            // 将取出的值同步到用户记录数据中
            addHistoryToRedis(param.getUserName(),resultData);
        }
        // 取地理位置
        Set<Integer> resultLocationList = getResultLocationList(param);
        if(!CollectionUtils.isEmpty(resultLocationList)){
            resultData.addAll(resultLocationList);
            addHistoryToRedis(param.getUserName(),resultData);
        }
        Integer total = recommendAttribute.getView().getTotal();
        int needCount = total - resultData.size();
        // 随机取 db 中的数据
        Set<Integer>  dbList = getResultDBList(param,needCount);
        if(!CollectionUtils.isEmpty(dbList)){
            resultData.addAll(dbList);
            addHistoryToRedis(param.getUserName(),dbList);
        }
        if(dbList.size() < needCount){
            RecommendEnums historyDataKey = RecommendEnums.RECOMMEND_HISTORY_DATA;
            historyDataKey.setKey(param.getUserName());
            redisService.deleteKey(historyDataKey);
        }
        RecommendEnums tempDataKey = RecommendEnums.RECOMMEND_TEMP_DATA;
        tempDataKey.setKey(param.getUserName());
        redisService.deleteKey(tempDataKey);
        // List<Integer> history = new ArrayList<>();
        // top 需要查询大于30分钟过期的数据 按展示顺序存放到List中 将top 中的数据 在内存中和 已看过得历史数据 取交集。
        // 点赞量最高的数据 取 不为0的前200条 需要再内存中比对
        // 最新数据 每次定时任务跑完需要将 最新列表置空，新增加的数据进入redis list 中，需要取出和历史数据做交集
        // 地域数据直接存储到 redis set 中和历史数据取交集 保存临时集合中 随机取几个值 数据删除时需要删除该set中的值
        // 数据库中的所有数据 存放到redis 中 随机取 删除时需要维护 和 历史取交集保存到临时set中，随机取，需要维护删除
        // 将最后的拿出的ID 到数据库中查出数据
        // 当随机出的数量为0时，直接清空用户历史记录，再次随机，如果为空则返回为空（判断 dbdata 为空则直接返回）
        // 定时任务 10 分钟一次，需要刷新 top, 点赞量,清空最新数据,刷新地域数据,刷新数据库
    /*    List<Integer> topped = redisService.getList(RecommendEnums.RECOMMEND_TOP_DATA, 0L, -1L);
        Set<Integer> integers = redisService.randomMembers(RecommendEnums.RECOMMEND_DB_DATA, 5);
        if (!CollectionUtils.isEmpty(topped)) {
            integers.add(topped.get(0));
        }
        redisService.addSet(RecommendEnums.RECOMMEND_HISTORY_DATA, integers);
        redisService.differenceAndStore(RecommendEnums.RECOMMEND_LIKES_DATA, RecommendEnums.RECOMMEND_HISTORY_DATA, RecommendEnums.RECOMMEND_TEMP_DATA);
        Set<Integer> integers1 = redisService.randomMembers(RecommendEnums.RECOMMEND_TEMP_DATA, 5);
*/
        return resultData;
    }

    private Set<Integer> getResultDBList(RecommendParam param,Integer needCount) {
        RecommendEnums tempDataKey = RecommendEnums.RECOMMEND_TEMP_DATA;
        tempDataKey.setKey(param.getUserName());
        redisService.deleteKey(tempDataKey);
        RecommendEnums dbDataKey = RecommendEnums.RECOMMEND_DB_DATA;
        RecommendEnums historyDataKey = RecommendEnums.RECOMMEND_HISTORY_DATA;
        historyDataKey.setKey(param.getUserName());
        redisService.differenceAndStore(dbDataKey, historyDataKey, tempDataKey);
        return redisService.randomMembers(tempDataKey, needCount);
    }

    private Set<Integer> getResultLocationList(RecommendParam param) {
        RecommendEnums locationDataKey = RecommendEnums.RECOMMEND_LOCATION_DATA;
        locationDataKey.setKey(param.getProvince());
        RecommendEnums historyDataKey = RecommendEnums.RECOMMEND_HISTORY_DATA;
        historyDataKey.setKey(param.getUserName());
        RecommendEnums tempDataKey = RecommendEnums.RECOMMEND_TEMP_DATA;
        tempDataKey.setKey(param.getUserName());
        redisService.deleteKey(tempDataKey);
        redisService.differenceAndStore(locationDataKey, historyDataKey, tempDataKey);
        Integer locationCount = recommendAttribute.getView().getLocation();
        return redisService.randomMembers(tempDataKey, locationCount);
    }

    private <V extends Collection<Integer>> void addHistoryToRedis(String userName,V historyIncrData) {
        RecommendEnums historyDataKey = RecommendEnums.RECOMMEND_HISTORY_DATA;
        historyDataKey.setKey(userName);
        Long historyCount = recommendAttribute.getView().getHistory();
        redisService.addSetClear(historyDataKey,historyIncrData,historyCount);
    }

    private List<Integer> getLikesList(Set<Integer> historyData) {
        List<Integer> resultLikes= new ArrayList<>();
        RecommendEnums likesData = RecommendEnums.RECOMMEND_LIKES_DATA;
        List<Integer> likesDataList = redisService.getList(likesData, START, END);
        if(!CollectionUtils.isEmpty(likesDataList) && !CollectionUtils.isEmpty(historyData) ){
            likesDataList.removeAll(historyData);
        }
        Integer likes = recommendAttribute.getView().getLikes();
        if(likesDataList.size() > likes){
            for (int i = 0; i < likes; i++) {
                resultLikes.add(likesDataList.get(i));
            }
        }else{
            resultLikes.addAll(likesDataList);
        }
        return resultLikes;
    }

    private List<Integer> getResultNewsList(Set<Integer> historyData, int needSize) {
        List<Integer> resultNews= new ArrayList<>();
        RecommendEnums newsData = RecommendEnums.RECOMMEND_NEWS_DATA;
        List<Integer> newsDataList = redisService.getList(newsData, START, END);
        if(!CollectionUtils.isEmpty(newsDataList) && !CollectionUtils.isEmpty(historyData) ){
            newsDataList.removeAll(historyData);
        }
        Integer news = recommendAttribute.getView().getNews();
        news = news + needSize;
        if(newsDataList.size() > news){
            for (int i = 0; i < news; i++) {
                resultNews.add(newsDataList.get(i));
            }
        }else {
            resultNews.addAll(newsDataList);
        }
        return resultNews;
    }

    private List<Integer> getResultTopList(Set<Integer> historyData) {
        List<Integer> resultTop = new ArrayList<>();
        // 取top
        List<Integer> topData = getTopData();
        // 和 历史数据作交集
        if(!CollectionUtils.isEmpty(topData) && !CollectionUtils.isEmpty(historyData) ){
            topData.removeAll(historyData);
        }
        Integer top = recommendAttribute.getView().getTop();
        if(topData.size() > top){
            for (int i = 0; i < top; i++) {
                resultTop.add(topData.get(i));
            }
        }else{
            resultTop.addAll(topData);
        }
        return resultTop;
    }

    public Set<Integer> getHistoryData(String userName){
        RecommendEnums historyData = RecommendEnums.RECOMMEND_HISTORY_DATA;
        RedisKeys redisKeys = historyData.setKey(userName);
        return redisService.getAllMembers(redisKeys);
    }

    public List<Integer> getTopData(){
        RecommendEnums topData = RecommendEnums.RECOMMEND_TOP_DATA;
       return  redisService.getList(topData, START, END);
    }

    @Override
    public void addRecommendData(Recommend param) {
        // 增加最新，地域，DB
        RecommendEnums newsDataKey = RecommendEnums.RECOMMEND_NEWS_DATA;
        newsDataKey.setValue(param.getRecommendId());

        RecommendEnums locationDataKey = RecommendEnums.RECOMMEND_LOCATION_DATA;
        locationDataKey.setKey(param.getProvince());
        locationDataKey.setValue(param.getRecommendId());

        RecommendEnums dbDataKey = RecommendEnums.RECOMMEND_DB_DATA;
        dbDataKey.setValue(param.getRecommendId());

        redisService.addList(newsDataKey);
        redisService.addMembers(locationDataKey);
        redisService.addMembers(dbDataKey);

    }

    @Override
    public void deleteRecommendData(Recommend param) {
        // top, 最新, 点赞的，地域，DB
        RecommendEnums topDataKey = RecommendEnums.RECOMMEND_TOP_DATA;
        topDataKey.setValue(param.getRecommendId());

        RecommendEnums newsDataKey = RecommendEnums.RECOMMEND_NEWS_DATA;
        newsDataKey.setValue(param.getRecommendId());

        RecommendEnums likesDataKey = RecommendEnums.RECOMMEND_LIKES_DATA;
        likesDataKey.setValue(param.getRecommendId());

        RecommendEnums locationDataKey = RecommendEnums.RECOMMEND_LOCATION_DATA;
        locationDataKey.setKey(param.getProvince());
        locationDataKey.setValue(param.getRecommendId());


        RecommendEnums dbDataKey = RecommendEnums.RECOMMEND_DB_DATA;
        dbDataKey.setValue(param.getRecommendId());

        redisService.deleteList(topDataKey);
        redisService.deleteList(newsDataKey);
        redisService.deleteList(likesDataKey);
        redisService.removeMembers(locationDataKey);
        redisService.removeMembers(dbDataKey);
    }

    @Override
    public void addTopped(Recommend param) {
        // top
        RecommendEnums topDataKey = RecommendEnums.RECOMMEND_TOP_DATA;
        topDataKey.setValue(param.getRecommendId());
        redisService.addList(topDataKey);
    }

    @Override
    public void removeTopped(Recommend param) {
        RecommendEnums topDataKey = RecommendEnums.RECOMMEND_TOP_DATA;
        topDataKey.setValue(param.getRecommendId());
        redisService.deleteList(topDataKey);
    }
}